#include "QuadTreeHojasComp.h"

QuadTreeHojasComp::QuadTreeHojasComp():QuadTreeHojas() {
}

QuadTreeHojasComp::~QuadTreeHojasComp() {

}



void QuadTreeHojasComp::guardar(ofstream & salida){
    int comprimido = 0;
    int tamanio = arbol.size();
    if (tamanio == 0)  //la raiz es una hoja
		salida.write((char *)&colorhomo,sizeof(int));
	else {
        salida.write((char *)&HOJASC,sizeof(int));
		salida.write((char *)&tamanio,sizeof(int));
        vector<int> datos = cargardatos();
        if (comprimir(datos)){
            guardarcomprimido(datos,salida);
        }
        else {
            double aux = 0;
            int cero = 0;
            salida.write((char*)&cero,sizeof(int));//no es comprimido
            for (int i = 0; i < tamanio; i++){
    			aux = concatenar(arbol[i].first);
    			salida.write((char *)&aux,sizeof(double));
    			salida.write((char *)&arbol[i].second,sizeof(int));
            }
        }
    }
}

bool QuadTreeHojasComp::comprimir(vector<int> & datos) {
    if (((datos.size() < 255) && ( ((3 * arbol.size()) / 4) > datos.size())) 
    || ((datos.size() < 65535) && ((arbol.size() / 2) > datos.size()) ))
        return true;
    return false;
}

void QuadTreeHojasComp::insertarordenado(vector<int> &arreglo, int dato) {
    if (arreglo.size() == 0)
        arreglo.push_back(dato);
    else
        if(dato < arreglo[0]){
            vector<int>::iterator it = arreglo.begin();
            arreglo.insert(it,dato);
        }
        else
            if(dato > arreglo[arreglo.size()-1])
                arreglo.push_back(dato);
            else {
                int pos = buscarbin(arreglo,0,arreglo.size()-1,dato);
                if (arreglo[pos] != dato) {
                    vector<int>::iterator it = arreglo.begin();
                    it+=pos;
                    arreglo.insert(it,dato);
                }
            }
}

int QuadTreeHojasComp::buscarbin(vector<int> &arreglo,int principio, int final, int buscado) {
   if (principio > final) {
      return principio;
   }
   else{
        int mitad = principio + ((final-principio) / 2);
        if (arreglo[mitad] > buscado)
               return buscarbin(arreglo,principio,mitad-1,buscado);
           else
               if (arreglo[mitad] < buscado)
                   return buscarbin(arreglo,mitad+1,final,buscado);
               else
                   return mitad;
    }
}

vector<int> QuadTreeHojasComp::cargardatos() {
    vector<int> retorno;
    for (int i=0; i<arbol.size();i++) {
         insertarordenado(retorno,arbol[i].second);
    }
    return retorno;
}

unsigned char  QuadTreeHojasComp::buscarchar(vector<int> datos,int buscado)  {
    unsigned char retorno;
    retorno = buscarbin(datos,0,datos.size(),buscado);
    return retorno;
}

unsigned short QuadTreeHojasComp::buscarshort(vector<int> &repeticiones,int buscado) {
    int bin = buscarbin(repeticiones,0,repeticiones.size(),buscado);
    return bin;
}

vector<unsigned char> QuadTreeHojasComp::reacomodarchar(vector<int> &datos) {
    vector<unsigned char> retorno;
    for (int i = 0; i<arbol.size();i++) {
        int dato = arbol[i].second;
        unsigned char aux = buscarchar(datos,dato);
        retorno.push_back(aux);
    }
    return retorno;
}

vector<unsigned short> QuadTreeHojasComp::reacomodarshort(vector<int> &repeticiones) {
    vector<unsigned short> retorno;
    for (int i = 0; i<arbol.size();i++) {
        unsigned short aux = buscarshort(repeticiones,arbol[i].second);
        retorno.push_back(aux);
    }
    return retorno;
}

void QuadTreeHojasComp::guardarcomprimido(vector<int> & datos,ofstream & salida){
    int tipo = 0;
    vector<unsigned char> chars;
    vector<unsigned short> shorts;
    if(datos.size() < 255) {
        chars = reacomodarchar(datos);
        tipo = sizeof(char);
    }
    else
        if (datos.size() < 65535) {
            shorts = reacomodarshort(datos);
            tipo = sizeof(unsigned short);
        }

    int uno = 1;
    salida.write((char*)&uno,sizeof(int));//guarda un 1 por q es comprimido
    salida.write((char*)&tipo,sizeof(int));//guardo el tipo de dato q se utiliza: 1 char 2 short
    double aux = 0;
    for (int i = 0; i<arbol.size();i++) {//guarda el arbol
        aux = concatenar(arbol[i].first);
        salida.write((char *)&aux,sizeof(double));

        if (tipo == sizeof(char))
    	   salida.write((char *)&chars[i],sizeof(char));
    	else
    	   salida.write((char *)&shorts[i],sizeof(unsigned short));
    }
    int tamanio = datos.size();
    salida.write((char *)&tamanio,sizeof(int));//guardo tamanio de paleta
    for (int i = 0; i < tamanio; i++){
        salida.write((char *)&datos[i],sizeof(int));//guardo la paleta
    }
}



void QuadTreeHojasComp::recuperar(ifstream & entrada) {
	int tamanio = 0;
	entrada.read((char *)&tamanio,sizeof(int));
	if (tamanio == 1) {//caso raiz es hoja
		entrada.read((char *)&colorhomo,sizeof(int));
	}
	else {
        int comprimido = -1;
        entrada.read((char *)&comprimido,sizeof(int));
        if (comprimido == 1)
            recuperarcomprimido(entrada,tamanio);
        else {
            double nodo;
		    int dato;
    		for (int i = 0; i < tamanio; i++) {
    			entrada.read((char *)&nodo,sizeof(double));
    			entrada.read((char *)&dato,sizeof(int));
    			setnodo(nodo,dato);
    		}
        }
	}
}

void QuadTreeHojasComp::recuperarcomprimido(ifstream & entrada,int tamanio) {
    int tipo = 0;
    entrada.read((char *)&tipo,sizeof(int));
    if (tipo == sizeof(char))
        recuperarchar(entrada,tamanio);
    else
        if (tipo == sizeof(unsigned short))
            recuperarshort(entrada,tamanio);
    recuperardatos(entrada);
}

void QuadTreeHojasComp::recuperarchar(ifstream & entrada, int tamanio) {
    double nodo;
    unsigned char dato;
    for (int i = 0; i < tamanio; i++) {
    	entrada.read((char *)&nodo,sizeof(double));
    	entrada.read((char *)&dato,sizeof(char));
    	setnodo(nodo,(int)dato);
    }
}

void QuadTreeHojasComp::recuperarshort(ifstream & entrada, int tamanio) {
    double nodo;
    unsigned short dato;
    for (int i = 0; i < tamanio; i++) {
    	entrada.read((char *)&nodo,sizeof(double));
    	entrada.read((char *)&dato,sizeof(unsigned short));
    	setnodo(nodo,(int)dato);
    }
}

void QuadTreeHojasComp::recuperardatos(ifstream & entrada) {
    vector<int> datos;
    int tamanio = -1, dato = -1;
    entrada.read((char *)&tamanio,sizeof(int));//leo el tamanio de la cantidad de datos
    for (int i = 0; i < tamanio; i++){
        entrada.read((char *)&dato,sizeof(int));
        datos.push_back(dato);
    }
    for (int i = 0; i < arbol.size(); i++)//intercambio el dato original con el apuntador q recuperamos anteriormente
        arbol[i].second = datos[arbol[i].second];
}
